iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
Rust

Rust 後端入門系列 第 3

Day 3 Rust 基礎語法(2): 函數、結構體與模組

  • 分享至 

  • xImage
  •  

上回我們學習變數、條件控制以及多種迴圈。

現在我們來讓 Rust 程式擁有更高的可讀性,我們將學習函數、結構體與模組。


函數

我們可以使用 fn 關鍵字定義一個函數,當有一部分程式碼重複時,我們就能使用函數,讓程式變得簡短更容易理解。或是想要將特定目的的程式碼分離出來,也可以使用函數。

我們定義一個 add 函數,用來處理相加,有 a 和 b 兩個參數,資料形態是32 bit的整數,回傳一個32 bit的整數。

有些人可能發現, a + b 沒有分號,這不是錯誤,無分號表示回傳這個表達式的值。

fn main() {
    let sum = add(5, 3);
    println!("5 + 3 = {}", sum);
}

fn add(a: i32, b: i32) -> i32 {
    a + b
}

結構體

我們可以使用 struct 關鍵字來指定資料形態,用來將高關聯的內容組合在一起,清楚表達各個欄位間的關係。

下面我們宣告一個 User 結構體,有 name、email、password的欄位,可以清楚理解欄位分別儲存了User的名字、電子信箱和密碼。

緊接著是使用 User 結構體建立一個 user1,並寫入各欄位的資訊。

最後,我們輸出剛才建立的 user1每個欄位的內容。

struct User {
    name: String,
    email: String,
    password: String,
}

fn main() {
    let user1 = User {
        name: String::from("user1"),
        email: String::from("user1@a.com"),
        password: String::from("12345678"),
    };

    println!("{} {} {}", user1.name, user1.email, user1.password);
}

列舉

enum 關鍵字可以用來表示一組互斥的選擇,適合用來表示狀態或選項。在本文的最後,將展示 enum的強大之處,先用一個簡單的範例呈現 enum的使用方式。

我們定義一個 enum,用來處理方向,方向只會是上下左右其中一種,相當適合使用 enum。可以注意到我們在 enum 前加上 #[derive(Debug)] ,這是因為我們在後面使用到 {:?} 用來輸出 enum 的欄位名稱。

#[derive(Debug)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

fn main() {
    let directions = vec![
        Direction::Down,
        Direction::Right,
        Direction::Up,
        Direction::Left,
        Direction::Down,
    ];

    for dir in directions {
        println!("{:?}", dir);
    }
}

enum 與 match 的結合使用

接下來,我們將 enum 和 match 一起使用,讓它們更加強大。

enum 除了先前的使用方式,還可以夾帶不同資料形態的資料。

match 取代了許多if,看起來更加簡潔清晰。

這是一個處理API接收到的響應

  • Success代表成功,String夾帶用戶的資料
  • ClientError代表用戶端發生錯誤,u16用來放置狀態碼,String儲存錯誤訊息。當狀態碼是404時,特別處理,顯示「找不到資源」的錯誤訊息
  • ServerError代表伺服器發生錯誤,用16 bit的無號整數放置狀態碼
  • NetworkError代表網路錯誤
enum ApiResponse {
    Success(String),
    ClientError(u16, String),
    ServerError(u16),
    NetworkError,
}

fn handle_response(response: ApiResponse) -> String {
    match response {
        ApiResponse::Success(data) => {
            format!("成功,{}", data)
        }
        ApiResponse::ClientError(code, msg) if code == 404 => {
            format!("找不到資源:{}", msg)
        }
        ApiResponse::ClientError(code, msg) => {
            format!("用戶端錯誤 {}:{}", code, msg)
        }
        ApiResponse::ServerError(code) => {
            format!("伺服器錯誤:{}", code)
        }
        ApiResponse::NetworkError => {
            "連線失敗,請檢查網路設定".to_string()
        }
    }
}

fn main() {
    let responses = vec![
        ApiResponse::Success("用戶資料".to_string()),
        ApiResponse::ClientError(404, "用戶不存在".to_string()),
        ApiResponse::ClientError(429, "速率限制,請稍候再試".to_string()),
        ApiResponse::ServerError(500),
        ApiResponse::NetworkError,
    ];

    for response in responses {
        println!("{}", handle_response(response));
    }
}

模組

當要開發大型專案時,mod 是不可或缺的,可以將複雜的程式模組拆分到各別的檔案中。 如果我們在同一個檔案中全使用 fn 拆分,可能會因為重複命名相同的函數,導致程式錯誤。

雖然我們之前說過,mod 可以分拆到不同檔案中,不過我們其實能在同一個程式中使用 mod。

底下是一個 mod 的使用範例,用來輸出數字的平方和立方。

要注意,不論是在同一個檔案或不同檔案,想在要 mod 區塊外使用內部的函數,就必須加上 pub。

mod math {
    pub fn square(n: i32) -> i32 {
        n * n
    }

    pub fn cube(n: i32) -> i32 {
        n * n * n
    }
}

fn main() {
    println!("2 的平方是 {}", math::square(2));
    println!("2 的立方是 {}", math::cube(2));
}

通常的 mod 使用方式,我們在同一個目錄下,拆分出一個新檔案 math.rs。

在 main.rs 使用 mod math ,來引用 math.rs 的內容。

// main.rs
mod math;  // 引用 math.rs 檔案

fn main() {
    println!("2 的平方是 {}", math::square(2));
    println!("2 的立方是 {}", math::cube(2));
}

// math.rs
pub fn square(n: i32) -> i32 {
    n * n
}

pub fn cube(n: i32) -> i32 {
    n * n * n
}


上一篇
Day 2 Rust 基礎語法(1): let、if、match 與三種迴圈
下一篇
Day 4 Rust 所有權與借用
系列文
Rust 後端入門4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言